package com.fuyun.rpc.proxy;


import cn.hutool.core.util.IdUtil;
import com.fuyun.rpc.config.RpcServiceConfig;
import com.fuyun.rpc.dto.RpcReq;
import com.fuyun.rpc.dto.RpcResp;
import com.fuyun.rpc.enums.RpcRespStatus;
import com.fuyun.rpc.exception.RpcException;
import com.fuyun.rpc.transmission.RpcClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

/**
 * @Author fuyun。
 * @Date 2025/8/21 15:30。
 * @Description: 客户端动态代理。
 **/
public class RpcClientProxy implements InvocationHandler {

    private final RpcClient rpcClient;
    private final RpcServiceConfig rpcServiceConfig;


    public RpcClientProxy(RpcClient rpcClient) {
        this(rpcClient, new RpcServiceConfig());
    }

    public RpcClientProxy(RpcClient rpcClient, RpcServiceConfig rpcServiceConfig) {
        this.rpcClient = rpcClient;
        this.rpcServiceConfig = rpcServiceConfig;
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcReq req = RpcReq.builder()
                .requestId(IdUtil.fastSimpleUUID())
                .interfaceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .params(args)
                .parameterTypes(method.getParameterTypes())
                .version(rpcServiceConfig.getVersion())
                .group(rpcServiceConfig.getGroup())
                .build();

        RpcResp<?> rpcResp = rpcClient.sendReq(req);
        check(req, rpcResp);
        return rpcResp.getData();
    }

    private void check(RpcReq req, RpcResp<?> rpcResp) {
        if (rpcResp == null) {
            throw new RpcException("rpc response is null");
        }

        if (!Objects.equals(req.getRequestId(), rpcResp.getRequestId())) {
            throw new RpcException("rpc response request id is duplicated");
        }

        if (RpcRespStatus.isFailed(rpcResp.getCode())) {
            throw new RpcException("rpc response status is failed");
        }

    }

}
